package com.yhy.wxzn.wcs.device.rgv.core.plc;

import cn.hutool.core.util.StrUtil;
import com.yhy.wxzn.common.enums.HoistStatus;
import com.yhy.wxzn.common.utils.SpringUtil;
import com.yhy.wxzn.common.utils.WcsUtils;
import com.yhy.wxzn.wcs.SystemInit;
import com.yhy.wxzn.wcs.device.plc.bean.HoistPlc;
import com.yhy.wxzn.wcs.device.plc.bean.XJPHoistPlc;
import com.yhy.wxzn.wcs.device.plc.task.PlcTaskThread;
import com.yhy.wxzn.wcs.device.plc.xj.XjPlcDriver;
import com.yhy.wxzn.wcs.device.rgv.bean.Coord;
import com.yhy.wxzn.wcs.device.rgv.bean.FourWayRgv;
import com.yhy.wxzn.wcs.device.rgv.bean.Lifter;
import com.yhy.wxzn.wcs.device.rgv.bean.Node;
import com.yhy.wxzn.wcs.device.rgv.core.lock.LockNodeManager;
import com.yhy.wxzn.wcs.entity.WcsDeviceInfo;
import com.yhy.wxzn.wcs.entity.WcsInboundDetail;
import com.yhy.wxzn.wcs.entity.WcsOutboundDetail;
import com.yhy.wxzn.wcs.entity.WcsTask;
import com.yhy.wxzn.wcs.enums.LifterLocation;
import com.yhy.wxzn.wcs.enums.TaskType;
import com.yhy.wxzn.wcs.service.impl.WcsDeviceInfoServiceImpl;
import com.yhy.wxzn.wcs.service.impl.WcsTaskServiceImpl;
import com.yhy.wxzn.wcs.utils.PlcUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.yhy.wxzn.wcs.SystemInit.*;

/**
 * 111
 */
@Slf4j
@Getter
public class ToLifer {

    private static final Map<Lifter,ToLifer> toLiferMap  = new ConcurrentHashMap<>();
    private Lifter lifter;
    private XJPHoistPlc hoistPlc;
    private XjPlcDriver plcDriver;

    private volatile int lifterTaskId = 0;

    private ToLifer(Lifter lifter){
        this.lifter = lifter;
        this.plcDriver = liferDriverMap.get(liferToDeviceIdMap.get(lifter));
        this.hoistPlc = liferPlcMap.get(liferToDeviceIdMap.get(lifter));
    }

    public static ToLifer getInstance(Lifter lifter){
        ToLifer toLifer = toLiferMap.get(lifter);
        if (toLifer == null){
            toLifer = new ToLifer(lifter);
            toLiferMap.put(lifter,toLifer);
        }
        return toLifer;
    }


    public void carCallLifer1(FourWayRgv rgv, int z){
        log.info("车{}调提升机{}1,开始",rgv.toString(),lifter.toString());
        checkAndLockLifter(rgv);


        if (hoistPlc.getLayer() != z){
            this.lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
            LifterTaskType taskType  = LifterTaskType.车换层;
            int startFloor =  hoistPlc.getLayer();
            int endFloor = z ;

            callLifter(lifterTaskId
                    ,taskType
                    ,startFloor
                    ,endFloor);

            carWaitLiferFinish(rgv,z,lifterTaskId,true);
        }




    }

    public void carCallLifer2(FourWayRgv rgv, int z){
        log.info("车{}调提升机{}2,开始",rgv.toString(),lifter.toString());

        WcsUtils.sleep(3000);
        retryCheckLiferStatus(rgv);

        int lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
        LifterTaskType taskType  = LifterTaskType.车换层;
        int startFloor =  hoistPlc.getLayer();
        int endFloor = z ;

        callLifter(lifterTaskId
                ,taskType
                ,startFloor
                ,endFloor);

        carWaitLiferFinish(rgv,z,lifterTaskId,false);

        //小车行驶完成一段路径后自动解锁所有其他节点
    }

    private void retryCheckLiferStatus(FourWayRgv rgv) {
        boolean retry = true;
        while (retry){

            rgv.checkIntercept("ToLifer.retryCheckLiferStatus");

            if (!isReady()) {
                WcsUtils.sleep(1000);
                continue;
            }
            retry = false;
        }
    }


    private void carCallLifer(FourWayRgv rgv, int z, int lifterPlcTaskId) {
        int sourceFloor = rgv.getCurLocation().getZ();
        log.info("车{}调提升机{}1,下发命令,sourceFloor{},sourceLifterLocation{}" +
                        ",targetFloor{},targetLifterLocation{},type{},lifterPlcTaskId{}"
                ,rgv.toString(),lifter.toString(),sourceFloor,LifterLocation.INSIDE.getDescription()
        ,z,LifterLocation.INSIDE.getDescription(),3,lifterPlcTaskId);


        callLifter(sourceFloor, LifterLocation.INSIDE, z, LifterLocation.INSIDE
                , 3, lifterPlcTaskId);
    }

    /**
     *
     * @param sourceFloor
     * @param sourceLifterLocation
     * @param targetFloor
     * @param targetLifterLocation
     * @param type 任务类型 1入库 2出库 3跨层
     * @param lifterPlcTaskId 1-99999循环，同一容器多段任务的任务号不同
     */
    private void callLifter(int sourceFloor, LifterLocation sourceLifterLocation,
                           int targetFloor,LifterLocation targetLifterLocation,
                           int type, int lifterPlcTaskId){
        byte[] bytes = PlcUtils.taskHoistByte(sourceFloor, sourceLifterLocation, targetFloor, targetLifterLocation
                , type, lifterPlcTaskId);

        plcDriver.write(hoistPlc.getWriteAddress(),bytes);
    }


    private void carWaitLiferFinish(FourWayRgv rgv, int z, int lifterPlcTaskId, boolean b) {
        while (true){
            if (b){
                rgv.checkInterceptAndReleaseLifterLock("ToLifer.waitLiferFinish",lifter);
            }  else {
                rgv.checkIntercept("ToLifer.waitLiferFinish");
            }

            if (hoistPlc.getLayer() == z
                    && hoistPlc.getTaskStatus() == 1
                    && hoistPlc.getTaskNoR() == lifterPlcTaskId) break;

            WcsUtils.sleep(1000);
        }
        //清除上一个任务
        plcDriver.write("HD5009",1);
    }

    private void checkAndLockLifter(FourWayRgv rgv) {
        boolean retry = true;
        while (retry){
            rgv.checkIntercept("ToLifer.lockLifter");
            if (!isReady()) {
                WcsUtils.sleep(1000);
                continue;
            }
            LockNodeManager.lockLifter(lifter, rgv);

            if (!isReady()) {
                LockNodeManager.unlockLifter(lifter,rgv);
                WcsUtils.sleep(1000);
                continue;
            }
            retry = false;
        }
    }


    /**
     * 出入库任务调用提升机
     * @param task
     * @param programme
     * @param deviceInfo
     */
    public void taskCallLifter(WcsTask task, WcsInboundDetail programme, WcsDeviceInfo deviceInfo){
        /**
         * 起点层：入库，提升机起点方案层    出库 任务终点层
         * 终点层：入库，任务起点层       出库   提升机终点方案层
         */
        int sourceFloor = task.getTaskType().equals(TaskType.IN) ?  getLifterFloorByProgramme(programme.getSource()) : Node.parseCoord(task.getEnd()).getZ();
        int targetFloor = task.getTaskType().equals(TaskType.IN) ? Node.parseCoord(task.getStart()).getZ() : getLifterFloorByProgramme(programme.getTarget());
        LifterLocation sourceLifterLocation = LifterLocation.parseLifterLocation(programme.getSource(),deviceInfo,sourceFloor);
        LifterLocation targetLifterLocation =  LifterLocation.parseLifterLocation(programme.getTarget(),deviceInfo,targetFloor);
        int type  = parseTaskType(task);
        int lifterPlcTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();

        checkAndLockLifter(task);

        callLifter(sourceFloor
                ,sourceLifterLocation
                ,targetFloor
                ,targetLifterLocation
                ,type
                ,lifterPlcTaskId);

        taskWaitLiferFinish(task,targetFloor,lifterPlcTaskId);
    }


    /**
     * 出入库任务调用提升机
     * @param task
     * @param programme
     * @param deviceInfo
     */
    public void taskCallLifter(WcsTask task, WcsOutboundDetail programme, WcsDeviceInfo deviceInfo){
        /**
         * 起点层：入库，提升机起点方案层    出库 任务终点层
         * 终点层：入库，任务起点层       出库   提升机终点方案层
         */
        int sourceFloor = task.getTaskType().equals(TaskType.IN) ?  getLifterFloorByProgramme(programme.getSource()) : Node.parseCoord(task.getEnd()).getZ();
        int targetFloor = task.getTaskType().equals(TaskType.IN) ? Node.parseCoord(task.getStart()).getZ() : getLifterFloorByProgramme(programme.getTarget());
        LifterLocation sourceLifterLocation = LifterLocation.parseLifterLocation(programme.getSource(),deviceInfo,sourceFloor);
        LifterLocation targetLifterLocation =  LifterLocation.parseLifterLocation(programme.getTarget(),deviceInfo,targetFloor);
        int type  = parseTaskType(task);
        int lifterPlcTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();

        checkAndLockLifter(task);

        callLifter(sourceFloor
                ,sourceLifterLocation
                ,targetFloor
                ,targetLifterLocation
                ,type
                ,lifterPlcTaskId);

        taskWaitLiferFinish(task,targetFloor,lifterPlcTaskId);
    }


    /**
     * 出入库用，等待提升机任务解锁，并解锁
     * @param task
     * @param z
     * @param lifterPlcTaskId
     */
    private void taskWaitLiferFinish(WcsTask task, int z, int lifterPlcTaskId) {
        while (true){
            try {
                PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"taskWaitLiferFinish");
            }catch (Exception e){
                LockNodeManager.unlockLifter(lifter,task);
                throw new RuntimeException(e.getMessage());
            }

            if (   hoistPlc.getLayer() == z && hoistPlc.getTaskStatus() == 1 && hoistPlc.getTaskNoR() == lifterPlcTaskId) break;

            WcsUtils.sleep(1000);
        }

        //清除上一个任务
        plcDriver.write("HD5009",1);
        LockNodeManager.unlockLifter(lifter,task);
    }
    private int getLifterFloorByProgramme(String coords){
        String[] split = coords.split(";");
        return Node.parseCoord(split[0]).getZ();
    }

    /**
     * 1:入库载货(从一层外入库) 2:出库载货(每层到一层外) 3:车换层 4:车载货换层
     * @param task
     * @return
     */
    private int parseTaskType(WcsTask task){
        if (TaskType.IN.equals(task.getTaskType()))
            return 1;
        if (TaskType.OUT.equals(task.getTaskType()))
            return 2;
        throw new RuntimeException("出现未知任务类型");
    }


    private void checkAndLockLifter(WcsTask task) {
        boolean retry = true;
        while (retry){
            PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"checkAndLockLifter");
            if (!isReady()) {
                WcsUtils.sleep(1000);
                continue;
            }
            if (task.getTaskType() == TaskType.IN && hoistPlc.getIsHaveCargo()) {
                WcsUtils.sleep(1000);
                continue;
            }

            LockNodeManager.lockLifter(lifter, task);

            if (!isReady()) {
                LockNodeManager.unlockLifter(lifter,task);
                WcsUtils.sleep(1000);
                continue;
            }
            if (task.getTaskType() == TaskType.IN && hoistPlc.getIsHaveCargo()) {
                LockNodeManager.unlockLifter(lifter,task);
                WcsUtils.sleep(1000);
                continue;
            }
            retry = false;
        }
    }


    public boolean isReady(){

//        int faultCode = hoistPlc.getFaultCode();

        int autoStatus = hoistPlc.getStatus();

        int workStatus = hoistPlc.getWorkStatus();


//        if (faultCode != 0){
//           log.error( String.format("%s 提升机存在故障，故障为%s"
//                    , lifter.toString(),XJPHoistPlc.parseFaultCode(faultCode)));
//            return false;
//        }

        //todo 提升机左右是否有货，工位有货

        if (HoistStatus.Auto.getValue() != autoStatus) {
            log.error(String.format("%s 提升机不是联机自动状态，等待设备切换为联机自动状态", lifter.toString()));
            return false;
        }

        if (workStatus != 3){
            log.error(String.format("%s 提升机工作状态不为就绪，等待设备切换为就绪状态", lifter.toString()));
            return false;
        }


        return true;
    }


    /**
     * 出库
     *        不同层情况
     *                  小车驱动，任务调用提升机到小车层，小车等待提升机到达，行驶进去
     *        同层情况
     *               目前可以满足
     *
     *
     *
     * @param task
     * @param outProgramme
     * @param deviceInfo
     */
    public void outCallLifter(WcsTask task, WcsOutboundDetail outProgramme, WcsDeviceInfo deviceInfo) {

        this.lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
        LifterTaskType taskType  = LifterTaskType.车换层;
        int startFloor =  hoistPlc.getLayer();
        int endFloor = Node.parseCoord(task.getStart()).getZ() ;

        checkAndLockLifter(task);

        callLifter(lifterTaskId
                ,taskType
                ,startFloor
                ,endFloor);

        waitingRgvInAndLeave(task);


        this.lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
         taskType  = LifterTaskType.出库载货;
         startFloor =   Node.parseCoord(task.getStart()).getZ() ;
         endFloor = getLifterFloorByProgramme(outProgramme.getTarget()) ;



        callLifter(lifterTaskId
                ,taskType
                ,startFloor
                ,endFloor);

        taskWaitLiferFinish(task,endFloor,lifterTaskId);
    }

    private void waitingRgvInAndLeave(WcsTask task) {
        Integer rgvId = task.getProcessRgvDeviceCode();
        FourWayRgv rgv = fourWayRgvMap.get(rgvId);

        //等待车进入
        while (true){
            //不能解锁，无法知道车辆的位置
            PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"waitingRgvIntoLifter");

            //todo 添加有无车光电信号
            Node curLocation = new Node();
            try {
                curLocation = rgv.getCurLocation();
            }catch (Exception e){ }

            if ( curLocation.getX() == lifter.getX() && curLocation.getY() == lifter.getY()) break;

            WcsUtils.sleep(1000);
        }

        //等待车离开
        while (true){
            //不能解锁，无法知道车辆的位置
            PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"waitingRgvLeaveLifter");



            //todo 添加有无车光电信号
            Node curLocation = new Node();
            try {
                curLocation = rgv.getCurLocation();
            }catch (Exception e){ }
            if (curLocation.getX() != 0 && curLocation.getY() != 0 && (curLocation.getX() != lifter.getX() || curLocation.getY() != lifter.getY())) break;

            WcsUtils.sleep(1000);
        }


    }

    /**
     * 入库任务调用提升机
     * 任务调用提升机到小车的层
     *
     * @param task
     * @param inProgramme
     * @param deviceInfo
     */
    public void inCallLifter(WcsTask task, WcsInboundDetail inProgramme, WcsDeviceInfo deviceInfo) {
        this.lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
        LifterTaskType taskType  = LifterTaskType.入库载货;
        int startFloor =   getLifterFloorByProgramme(inProgramme.getSource());
        //到小车所在的层
        Node curLocation = null;
        while (true){
            task = SpringUtil.getBean(WcsTaskServiceImpl.class).getById(task.getTaskId());
            if (task.getProcessRgvDeviceCode() == null || task.getProcessRgvDeviceCode() == 0){
                WcsUtils.sleep(1000);
                continue;
            }
            try {
                 curLocation = fourWayRgvMap.get(task.getProcessRgvDeviceCode()).getCurLocation();
            }catch (Exception e){}
            if (curLocation!=null && curLocation.getZ() != 0) break;
        }
        int endFloor = curLocation.getZ();

        checkAndLockLifter(task);

        inPortCallLifter(lifterTaskId
                ,taskType
                ,startFloor
                ,endFloor);



        waitingRgvIn(task,endFloor,lifterTaskId);



        this.lifterTaskId = XJPHoistPlc.getAutoIncrementLiferTaskNo();
         taskType  = LifterTaskType.车换层;
         startFloor =  hoistPlc.getLayer();
         endFloor = Node.parseCoord(task.getStart()).getZ() ;



            callLifter(lifterTaskId
                    ,taskType
                    ,startFloor
                    ,endFloor);
        

        taskWaitLiferFinish(task,endFloor,lifterTaskId);
    }

    private void waitingRgvIn(WcsTask task, int endFloor, int lifterTaskId) {
        Integer rgvId = task.getProcessRgvDeviceCode();
        FourWayRgv rgv = fourWayRgvMap.get(rgvId);

        while (true){
            try {
               PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"taskWaitLiferFinish");
            }catch (Exception e){
                LockNodeManager.unlockLifter(lifter,task);
                throw new RuntimeException(e.getMessage());
            }

            if (   hoistPlc.getLayer() == endFloor && hoistPlc.getTaskStatus() == 1 && hoistPlc.getTaskNoR() == lifterTaskId) break;

            WcsUtils.sleep(1000);
        }

        //清除上一个任务
        plcDriver.write("HD5009",1);
        //等待车进入
        while (true){
            //不能解锁，无法知道车辆的位置
            PlcTaskThread.checkPLCInterrupt(task.getTaskId(),"waitingRgvIntoLifter");





            //todo 添加有无车光电信号
            Node curLocation = new Node();
            try {
                curLocation = rgv.getCurLocation();
            }catch (Exception e){ }

            if ( curLocation.getX() == lifter.getX() && curLocation.getY() == lifter.getY()) break;

            WcsUtils.sleep(1000);
        }
    }

    private void inPortCallLifter(int lifterPlcTaskId, LifterTaskType taskType, int startFloor, int endFloor) {
        //任务开始
        callLifter(lifterPlcTaskId, taskType, startFloor, endFloor);
    }

    private void callLifter(int lifterPlcTaskId, LifterTaskType taskType, int startFloor, int endFloor) {

        if (hoistPlc.getTaskNoR() != 0){
            //清除上一个任务
            clearLifterTake();
        }

        plcDriver.write("HD5008",1);
        byte[] cmds = new byte[8];
        cmds[0] = (byte) ((lifterPlcTaskId >> 8) & 0xFF);
        cmds[1] = (byte) (lifterPlcTaskId & 0xFF);
        cmds[2] = (byte) ((taskType.getCmd() >> 8) & 0xFF);
        cmds[3] = (byte) (taskType.getCmd() & 0xFF);
        cmds[4] = (byte) ((startFloor >> 8) & 0xFF);
        cmds[5] = (byte) (startFloor & 0xFF);
        cmds[6] = (byte) ((endFloor >> 8) & 0xFF);
        cmds[7] = (byte) (endFloor & 0xFF);
        plcDriver.write("HD5002",cmds);
        log.info("发送提升机任务成功，提升机任务id：{}，任务类型：{}，开始层：{}，结束层：{}",lifterPlcTaskId,taskType,startFloor,endFloor);
    }
    public void clearLifterTake(){
        log.info("{}发送任务完成信号",hoistPlc.getPlcId());
        plcDriver.write("HD5009",1);
    }



    /**
     * 1:入库载货(从一层外入库) 2:出库载货(每层到一层外) 3:车换层 4:车载货换层
     */
    @AllArgsConstructor
    @Getter
    public enum LifterTaskType {
        入库载货(1),
        出库载货(2),
        车换层(3),
        车载货换层(4);

        int cmd;
    }
}
